#include "graph.h"
#include <QtDataVisualization/QCategory3DAxis>
#include <QtDataVisualization/QValue3DAxis>
#include <QtDataVisualization/QBarDataProxy>
#include <QtDataVisualization/q3dscene.h>
#include <QtDataVisualization/Q3DCamera>
#include <QtDataVisualization/Q3DTheme>
#include <QtCore/QTime>
#include <QtWidgets/QComboBox>
#include <QtCore/qmath.h>

using namespace QtDataVisualization;

const QString celsiusString = QString(QChar(0xB0)) + "C";

//! [0]
MyGraph::MyGraph(Q3DBars *bargraph, ThreadManager* m)
    : m_graph(bargraph),
      m_xRotation(0.0f),
      m_yRotation(0.0f),
      m_fontSize(30),
      m_segments(4),
      m_subSegments(3),
      m_minval(-20.0f),
      m_maxval(20.0f),
      //! [1]
      m_temperatureAxis(new QValue3DAxis),
      m_rowAxis(new QCategory3DAxis),
      m_columnAxis(new QCategory3DAxis),
      m_primarySeries(new QBar3DSeries),
#if 0
      m_secondarySeries(new QBar3DSeries),
#endif
      m_barMesh(QAbstract3DSeries::MeshBevelBar),
      m_smooth(false),
      manager(m)
{
    for (int i=0;i<24;i++){
        for (int j=0;j<32;j++){
            m_temp[i][j]=0.0f;
        }
    }

    //! [2]
    m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualitySoftMedium);
    m_graph->activeTheme()->setBackgroundEnabled(false);
    m_graph->activeTheme()->setFont(QFont("Times New Roman", m_fontSize));
    m_graph->activeTheme()->setLabelBackgroundEnabled(true);
    m_graph->setMultiSeriesUniform(true);
    //! [2]

    for (int i=0,cnt=0; i<=3;i++) {
        bool flag=true;
        for (int j=0;flag&&j<10;j++) {
            if (cnt == 24) {
                flag = false;
                continue;
            }
            QString s;
            s.append("R_");s.append(i+'0');s.append(j+'0');
            m_rows<<s;
            cnt++;
        }
        if (!flag) {
            break;
        }
    }

    for (int i=0,cnt=0; i<=3;i++) {
        bool flag=true;
        for (int j=0;flag&&j<10;j++) {
            if (cnt == 32) {
                flag = false;
                continue;
            }
            QString s;
            s.append("C_");s.append(i+'0');s.append(j+'0');
            m_columns<<s;
            cnt++;
        }
        if (!flag) {
            break;
        }
    }

#if 0
    m_columns << "C_0" << "C_1" << "C_2"
             << "C_3" << "C_4" << "C_5"
             << "C_6" << "C_7" << "C_8"
             << "C_9" << "C_10" << "C_11";

    m_rows << "R_0" << "R_1" << "R_2"
            << "R_3" << "R_4" << "R_5"
            << "R_6" << "R_7";
#endif
    //! [3]
    m_temperatureAxis->setTitle("pixel temperature");
    m_temperatureAxis->setSegmentCount(m_segments);
    m_temperatureAxis->setSubSegmentCount(m_subSegments);
    m_temperatureAxis->setRange(m_minval, m_maxval);
    m_temperatureAxis->setLabelFormat(QString(QStringLiteral("%.1f ") + celsiusString));
    m_temperatureAxis->setLabelAutoRotation(30.0f);
    m_temperatureAxis->setTitleVisible(true);

    m_rowAxis->setTitle("row");
    m_rowAxis->setLabelAutoRotation(30.0f);
    m_rowAxis->setTitleVisible(true);
    m_columnAxis->setTitle("column");
    m_columnAxis->setLabelAutoRotation(30.0f);
    m_columnAxis->setTitleVisible(true);

    m_graph->setValueAxis(m_temperatureAxis);
    m_graph->setRowAxis(m_rowAxis);
    m_graph->setColumnAxis(m_columnAxis);
    //! [3]

    //! [8]
    m_primarySeries->setItemLabelFormat(QStringLiteral("pixel(celsis) - @colLabel @rowLabel: @valueLabel"));
    m_primarySeries->setMesh(QAbstract3DSeries::MeshBevelBar);
    m_primarySeries->setMeshSmooth(false);
#if 0
    m_secondarySeries->setItemLabelFormat(QStringLiteral("Helsinki - @colLabel @rowLabel: @valueLabel"));
    m_secondarySeries->setMesh(QAbstract3DSeries::MeshBevelBar);
    m_secondarySeries->setMeshSmooth(false);
    m_secondarySeries->setVisible(false);
#endif
    //! [8]

    //! [4]
    m_graph->addSeries(m_primarySeries);
#if 0
    m_graph->addSeries(m_secondarySeries);
#endif
    //! [4]

    //! [6]
    changePresetCamera();
    //! [6]

    //! [9]
    resetTemperatureData();
    //! [9]

    // Set up property animations for zooming to the selected bar
    //! [12]
    Q3DCamera *camera = m_graph->scene()->activeCamera();
    m_defaultAngleX = camera->xRotation();
    m_defaultAngleY = camera->yRotation();
    m_defaultZoom = camera->zoomLevel();
    m_defaultTarget = camera->target();

    m_animationCameraX.setTargetObject(camera);
    m_animationCameraY.setTargetObject(camera);
    m_animationCameraZoom.setTargetObject(camera);
    m_animationCameraTarget.setTargetObject(camera);

    m_animationCameraX.setPropertyName("xRotation");
    m_animationCameraY.setPropertyName("yRotation");
    m_animationCameraZoom.setPropertyName("zoomLevel");
    m_animationCameraTarget.setPropertyName("target");

    int duration = 1700;
    m_animationCameraX.setDuration(duration);
    m_animationCameraY.setDuration(duration);
    m_animationCameraZoom.setDuration(duration);
    m_animationCameraTarget.setDuration(duration);

    // The zoom always first zooms out above the graph and then zooms in
    qreal zoomOutFraction = 0.3;
    m_animationCameraX.setKeyValueAt(zoomOutFraction, QVariant::fromValue(0.0f));
    m_animationCameraY.setKeyValueAt(zoomOutFraction, QVariant::fromValue(90.0f));
    m_animationCameraZoom.setKeyValueAt(zoomOutFraction, QVariant::fromValue(50.0f));
    m_animationCameraTarget.setKeyValueAt(zoomOutFraction,
                                          QVariant::fromValue(QVector3D(0.0f, 0.0f, 0.0f)));
    //! [12]
}
//! [0]

MyGraph::~MyGraph()
{
    delete m_graph;
}

void MyGraph::resetTemperatureData()
{
    static bool first = true;
    //! [5]
    // Create data arrays
    QBarDataArray *dataSet = new QBarDataArray;
    QBarDataRow *dataRow;

#if 0
    QBarDataArray *dataSet2 = new QBarDataArray;
    QBarDataRow *dataRow2;
#endif
    dataSet->reserve(m_rows.size());
    for (int row = 0; row < m_rows.size(); row++) {
        // Create a data row
        dataRow = new QBarDataRow(m_columns.size());
#if 0
        dataRow2 = new QBarDataRow(m_columns.size());
#endif
        if (!first)
            for (int column = 0; column < m_columns.size(); column++) {
                // Add data to the row
                (*dataRow)[column].setValue(m_temp[row][column]);
#if 0
            (*dataRow2)[column].setValue(tempHelsinki[row][column]);
#endif
            }
        else {
            for (int column = 0; column < m_columns.size(); column++)
                (*dataRow)[column].setValue(0.0f);
            first = false;
        }
        // Add the row to the set
        dataSet->append(dataRow);
#if 0
        dataSet2->append(dataRow2);
#endif
    }

    // Add data to the data proxy (the data proxy assumes ownership of it)
    m_primarySeries->dataProxy()->resetArray(dataSet, m_rows, m_columns);
#if 0
    m_secondarySeries->dataProxy()->resetArray(dataSet2, m_rows, m_columns);
#endif
    //! [5]
}

void MyGraph::changeRange(int range)
{
    if (range >= m_rows.count())
        m_rowAxis->setRange(0, m_rows.count() - 1);
    else
        m_rowAxis->setRange(range, range);
}

void MyGraph::changeStyle(int style)
{
    QComboBox *comboBox = qobject_cast<QComboBox *>(sender());
    if (comboBox) {
        m_barMesh = QAbstract3DSeries::Mesh(comboBox->itemData(style).toInt());
        m_primarySeries->setMesh(m_barMesh);
#if 0
        m_secondarySeries->setMesh(m_barMesh);
#endif
    }
}

void MyGraph::changePresetCamera()
{
    m_animationCameraX.stop();
    m_animationCameraY.stop();
    m_animationCameraZoom.stop();
    m_animationCameraTarget.stop();

    // Restore camera target in case animation has changed it
    m_graph->scene()->activeCamera()->setTarget(QVector3D(0.0f, 0.0f, 0.0f));

    //! [10]
    static int preset = Q3DCamera::CameraPresetFront;

    m_graph->scene()->activeCamera()->setCameraPreset((Q3DCamera::CameraPreset)preset);

    if (++preset > Q3DCamera::CameraPresetDirectlyBelow)
        preset = Q3DCamera::CameraPresetFrontLow;
    //! [10]
}

void MyGraph::changeTheme(int theme)
{
    Q3DTheme *currentTheme = m_graph->activeTheme();
    currentTheme->setType(Q3DTheme::Theme(theme));
    emit backgroundEnabledChanged(currentTheme->isBackgroundEnabled());
    emit gridEnabledChanged(currentTheme->isGridEnabled());
    emit fontChanged(currentTheme->font());
    emit fontSizeChanged(currentTheme->font().pointSize());
}

void MyGraph::changeLabelBackground()
{
    m_graph->activeTheme()->setLabelBackgroundEnabled(!m_graph->activeTheme()->isLabelBackgroundEnabled());
}

void MyGraph::changeSelectionMode(int selectionMode)
{
    QComboBox *comboBox = qobject_cast<QComboBox *>(sender());
    if (comboBox) {
        int flags = comboBox->itemData(selectionMode).toInt();
        m_graph->setSelectionMode(QAbstract3DGraph::SelectionFlags(flags));
    }
}

void MyGraph::changeFont(const QFont &font)
{
    QFont newFont = font;
    m_graph->activeTheme()->setFont(newFont);
}

void MyGraph::changeFontSize(int fontsize)
{
    m_fontSize = fontsize;
    QFont font = m_graph->activeTheme()->font();
    font.setPointSize(m_fontSize);
    m_graph->activeTheme()->setFont(font);
}

void MyGraph::shadowQualityUpdatedByVisual(QAbstract3DGraph::ShadowQuality sq)
{
    int quality = int(sq);
    // Updates the UI component to show correct shadow quality
    emit shadowQualityChanged(quality);
}

void MyGraph::changeLabelRotation(int rotation)
{
    m_temperatureAxis->setLabelAutoRotation(float(rotation));
    m_columnAxis->setLabelAutoRotation(float(rotation));
    m_rowAxis->setLabelAutoRotation(float(rotation));
}

void MyGraph::setAxisTitleVisibility(bool enabled)
{
    m_temperatureAxis->setTitleVisible(enabled);
    m_columnAxis->setTitleVisible(enabled);
    m_rowAxis->setTitleVisible(enabled);
}

void MyGraph::setAxisTitleFixed(bool enabled)
{
    m_temperatureAxis->setTitleFixed(enabled);
    m_columnAxis->setTitleFixed(enabled);
    m_rowAxis->setTitleFixed(enabled);
}

//! [11]
void MyGraph::zoomToSelectedBar()
{
    m_animationCameraX.stop();
    m_animationCameraY.stop();
    m_animationCameraZoom.stop();
    m_animationCameraTarget.stop();

    Q3DCamera *camera = m_graph->scene()->activeCamera();
    float currentX = camera->xRotation();
    float currentY = camera->yRotation();
    float currentZoom = camera->zoomLevel();
    QVector3D currentTarget = camera->target();

    m_animationCameraX.setStartValue(QVariant::fromValue(currentX));
    m_animationCameraY.setStartValue(QVariant::fromValue(currentY));
    m_animationCameraZoom.setStartValue(QVariant::fromValue(currentZoom));
    m_animationCameraTarget.setStartValue(QVariant::fromValue(currentTarget));

    QPoint selectedBar = m_graph->selectedSeries()
            ? m_graph->selectedSeries()->selectedBar()
            : QBar3DSeries::invalidSelectionPosition();

    if (selectedBar != QBar3DSeries::invalidSelectionPosition()) {
        // Normalize selected bar position within axis range to determine target coordinates
        //! [13]
        QVector3D endTarget;
        float xMin = m_graph->columnAxis()->min();
        float xRange = m_graph->columnAxis()->max() - xMin;
        float zMin = m_graph->rowAxis()->min();
        float zRange = m_graph->rowAxis()->max() - zMin;
        endTarget.setX((selectedBar.y() - xMin) / xRange * 2.0f - 1.0f);
        endTarget.setZ((selectedBar.x() - zMin) / zRange * 2.0f - 1.0f);
        //! [13]

        // Rotate the camera so that it always points approximately to the graph center
        //! [15]
        qreal endAngleX = qAtan(qreal(endTarget.z() / endTarget.x())) / M_PI * -180.0 + 90.0;
        if (endTarget.x() > 0.0f)
            endAngleX -= 180.0f;
        float barValue = m_graph->selectedSeries()->dataProxy()->itemAt(selectedBar.x(),
                                                                        selectedBar.y())->value();
        float endAngleY = barValue >= 0.0f ? 30.0f : -30.0f;
        if (m_graph->valueAxis()->reversed())
            endAngleY *= -1.0f;
        //! [15]

        m_animationCameraX.setEndValue(QVariant::fromValue(float(endAngleX)));
        m_animationCameraY.setEndValue(QVariant::fromValue(endAngleY));
        m_animationCameraZoom.setEndValue(QVariant::fromValue(250));
        //! [14]
        m_animationCameraTarget.setEndValue(QVariant::fromValue(endTarget));
        //! [14]
    } else {
        // No selected bar, so return to the default view
        m_animationCameraX.setEndValue(QVariant::fromValue(m_defaultAngleX));
        m_animationCameraY.setEndValue(QVariant::fromValue(m_defaultAngleY));
        m_animationCameraZoom.setEndValue(QVariant::fromValue(m_defaultZoom));
        m_animationCameraTarget.setEndValue(QVariant::fromValue(m_defaultTarget));
    }

    m_animationCameraX.start();
    m_animationCameraY.start();
    m_animationCameraZoom.start();
    m_animationCameraTarget.start();
}
//! [11]

void MyGraph::changeShadowQuality(int quality)
{
    QAbstract3DGraph::ShadowQuality sq = QAbstract3DGraph::ShadowQuality(quality);
    m_graph->setShadowQuality(sq);
    emit shadowQualityChanged(quality);
}

//! [7]
void MyGraph::rotateX(int rotation)
{
    m_xRotation = rotation;
    m_graph->scene()->activeCamera()->setCameraPosition(m_xRotation, m_yRotation);
}

void MyGraph::rotateY(int rotation)
{
    m_yRotation = rotation;
    m_graph->scene()->activeCamera()->setCameraPosition(m_xRotation, m_yRotation);
}
//! [7]

void MyGraph::setBackgroundEnabled(int enabled)
{
    m_graph->activeTheme()->setBackgroundEnabled(bool(enabled));
}

void MyGraph::setGridEnabled(int enabled)
{
    m_graph->activeTheme()->setGridEnabled(bool(enabled));
}

void MyGraph::setSmoothBars(int smooth)
{
    m_smooth = bool(smooth);
    m_primarySeries->setMeshSmooth(m_smooth);
#if 0
    m_secondarySeries->setMeshSmooth(m_smooth);
#endif
}

void MyGraph::setSeriesVisibility(int enabled)
{
#if 0
    m_secondarySeries->setVisible(bool(enabled));
#endif
}

void MyGraph::setReverseValueAxis(int enabled)
{
    m_graph->valueAxis()->setReversed(enabled);
}

void MyGraph::setReflection(bool enabled)
{
    m_graph->setReflection(enabled);
}

void MyGraph::setData() {
    while (!manager->processor->matAvailbale) { // wait for mat
    }
    manager->isSettingData = true;
    for (int i=0;i<24;i++) {
        for (int j=0;j<32;j++) {
            m_temp[i][j] = manager->processor->mat[i][j];
            qDebug()<<i<<" "<<j<<m_temp[i][j];
        }
    }
    manager->isSettingData = false;
    resetTemperatureData();
}
